/**
 * 
 */
package com.oron3.bouncer.genericgl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

import android.os.Handler;

import com.oron3.bouncer.BouncyBallActivity;
import com.oron3.bouncer.Timer;
import com.oron3.bouncer.interaction.ClickEvent;
import com.oron3.bouncer.interaction.InteractiveEvent;
import com.oron3.bouncer.interaction.MotionEvent;
import com.oron3.bouncer.objects.GLBall;
import com.oron3.bouncer.objects.GLLine;
import com.oron3.bouncer.objects.GLShape;
import com.oron3.bouncer.objects.GLSphere;
import com.oron3.bouncer.objects.GameArc;
import com.oron3.bouncer.objects.shapes.Cylinder;
import com.oron3.bouncer.objects.shapes.Line;
import com.oron3.bouncer.objects.shapes.LineSegment;
import com.oron3.bouncer.objects.shapes.PPoint;
import com.oron3.bouncer.objects.shapes.Point;
import com.oron3.bouncer.objects.shapes.Vector;
import com.oron3.bouncer.objects.shapes.WorldArc;

/**
 * @author Ofek
 *
 */
public class BallWorld extends GLWorld {

	private final ArrayList<GLShape> objects = new ArrayList<>();
	private Point pressPoint;
	private final Timer addBallTimer = new Timer();
	private Timer dtTimer;
	private final long ballCreateInterval = 1000;
	private final Random rand = new Random();
	private Point previousPoint;
	private Handler handler;
	private GLLine touchRay;
	private GLLine prevTouchRay;
	private GLSphere sphere;
	private final GLBall ball;
	private final Point center;
	private final Timer touchTimer = new Timer();
	private final  Vector velocityVector = new Vector(0, 0, 0);
	private Point curr;
	private Point prev;
	private GLLine checkRay;

	private final WorldArc worldArc;
	private final ArrayList<GameArc> gameArcs = new ArrayList<>();
	private class ShootingSphere extends GLSphere {

		private Line centerLine;
		public float velocity=100;
		public ShootingSphere(final float x, final float y, final float z, final float radius, final float da) {
			super(x, y, z, radius, da);
		}

		public ShootingSphere(final Line touchLine, final float r, final float da) {
			super(touchLine.p1, r, da);
			centerLine = touchLine;
		}
		/* (non-Javadoc)
		 * @see com.oron3.bouncer.objects.GLSphere#step(float)
		 */
		@Override
		public void step(final float dt,final GLWorld glWorld) {
			center=center.add(centerLine.d.mul(dt*velocity));
		}

	}


	public BallWorld() {	
		center = new Point(0,0,0);
		worldArc = new WorldArc(center.sub(50,50,50), 100, 100, 100);
		final Random random = new Random();
		for ( int i = 0 ; i < 2 ;i++ ) {
			gameArcs .add(new GameArc(new Point(-50,random.nextInt(100)-50,random.nextInt(100)-50), 1+random.nextInt(40), 1+random.nextInt(40), 1+random.nextInt(40)));
		}
		for ( int i = 0 ; i < 2 ;i++ ) {
			gameArcs .add(new GameArc(new Point(random.nextInt(100)-50,-50,random.nextInt(100)-50), 1+random.nextInt(40), 1+random.nextInt(40), 1+random.nextInt(40)));
		}
		worldArc.setColor(WorldConstants.green);
		ball = new GLBall(new Point(5,5,5),0.82f);
		//		ball.setPosition(0,0,0);
		//		ball.setVelocity(0,2,10);
		objects.add(ball);
		rotationA = 45;
		rotationB = 45;
		final Handler handler2 = new Handler();
		handler2.postDelayed(new Runnable() {

			@Override
			public void run() {
				BouncyBallActivity.textView.setText(ball.toString());
				handler2.postDelayed(this, 250);
			}
		}, 250);
	}

	@Override
	public void setProjector(final Projector p) {
		super.setProjector(p);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.genericgl.GLWorld#draw(com.oron3.bouncer.genericgl.MatrixTrackingGL)
	 */
	@Override
	public void draw(final MatrixTrackingGL gl) {
		//		if (addBallTimer.hasElapsed(ballCreateInterval)) {
		//			balls.add(createNewBall());
		//			addBallTimer.restart();
		//		}
		//checkRay = new GLLine(ball.center, glRectangle.project(ball.center));

		if ( dtTimer==null ) {
			dtTimer=new Timer();
		}
		computeScene();
		drawObjects(gl);
	}

	/**
	 * 
	 */
	private void computeScene() {
		synchronized (ball) {
			float dt = dtTimer.elapsedSec();
			if ( dt==0 ) return;
			if (ball.v.isZero()) {
				ball.step(dt,this);
				return;
			}
			for ( final GameArc gameArc : gameArcs) {
				dt = gameArc.moveBall(this,ball, dt);
			}

			dt = worldArc.moveBall(this,ball,dt);

			if ( dt>0 ) {
				ball.step(dt,this);
			}
			dtTimer.restart();
		}
	}


	/**
	 * @param gl 
	 * 
	 */
	private void drawObjects(final MatrixTrackingGL gl) {
		worldArc.draw(gl);
		final Point eye = getEye();
		final LineSegment rayToBall = new LineSegment(eye,ball.center);
		final ArrayList<GLShape> drawBefore = new ArrayList<GLShape>();
		final ArrayList<GLShape> drawAfter = new ArrayList<GLShape>();
		for ( final GameArc gameArc : gameArcs) {
			if ( gameArc.intersection(rayToBall)!=null ) drawAfter.add(gameArc);
			else drawBefore.add(gameArc);
		}

		for ( final GLShape gameArc : drawBefore) {
			gameArc.draw(gl);
		}
		for (final GLShape o : objects) {
			o.draw(gl);
		}
		for ( final GLShape gameArc : drawAfter) {
			gameArc.draw(gl);
		}


		if ( touchRay!=null ) {
			touchRay.draw(gl);
		}


	}

	/**
	 * @return
	 */
	private GLBall createNewBall() {
		final Point point = new Point(10, getRandomFloat(), getRandomFloat());
		final GLBall ball = new GLBall(10, point.y, point.z);
		final PPoint polar = point.toPolar();
		final float velocitySize = getRandomFloat();
		ball.setVelocity(0, -(point.y / polar.r) * velocitySize, -(point.z / polar.r) * velocitySize);
		return ball;
	}

	/**
	 * @return
	 */
	private float getRandomFloat() {
		return -100 + rand.nextFloat() * 200;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.genericgl.GLWorld#onPress(com.oron3.bouncer.interaction.InteractiveEvent)
	 */
	@Override
	public boolean onPress(final InteractiveEvent e) {
		System.out.println("onPress");
		final Line touchLine = convertScreenToWorld(new Point(e.x, e.y, e.z));
		sphere = new ShootingSphere(touchLine,1,0.1f);
		prev = curr = (Point) touchLine.project(ball.center);
		velocityVector.set(0, 0, 0);
		touchTimer.restart();
		return true;
	}

	@Override
	public boolean onClick(final ClickEvent e) {
		return false;
	}

	@Override
	public boolean onDoubleClick(final ClickEvent e) {
		System.out.println("onDoubleClick");
		synchronized (ball) {
			ball.setPosition(30,30,50);
			ball.setVelocity(0,0,0);
		}
		return false;
	}

	@Override
	public boolean onMovement(final MotionEvent e) {
		return false;
	}

	@Override
	public boolean onDrag(final MotionEvent e) {
		final Line touchLine = convertScreenToWorld(new Point(e.x, e.y, e.z));
		prev = curr;
		synchronized (ball) {
			curr = (Point) touchLine.project(ball.center);
			if (curr.equals(prev)) return true;
			final float elapsedSec = touchTimer.elapsedSec()+0.001f;
			touchTimer.restart();
			velocityVector.set((curr.x-prev.x)/elapsedSec, (curr.y-prev.y)/elapsedSec, (curr.z-prev.z)/elapsedSec);
			if (ball.intersects(new Cylinder(touchLine,30))) {
				final Vector vector = new Vector(curr.sub(prev));
				if (worldArc.isInside(ball.center.add(vector))) 
					ball.move(vector);
				ball.setVelocity(ball.mu*velocityVector.x,ball.mu*velocityVector.y,ball.mu*velocityVector.z);
			} else if (!ball.intersects(new Cylinder(touchLine,40))){
				rotationA-=e.getMovey();
				if (rotationA<0) rotationA=0;
				if (rotationA>45) rotationA=45;
				rotationB+=e.getMovex();
			}
		}

		return true;
	}

	@Override
	public boolean onDragEnd(final InteractiveEvent e) {
		return false;
	}

	@Override
	public boolean onRelease(final InteractiveEvent e) {
		System.out.println("onRelease");

		System.out.println(ball);
		return false;
	}

	/* (non-Javadoc)
	 * @see com.oron3.bouncer.genericgl.GLWorld#getCenter()
	 */
	@Override
	public Point getCenter() {

		return center;
	}
	/* (non-Javadoc)
	 * @see com.oron3.bouncer.genericgl.GLWorld#getEye()
	 */
	@Override
	public Point getEye() {
		return super.getEye();
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */

	/* (non-Javadoc)
	 * @see java.lang.Iterable#iterator()
	 */
	@Override
	public Iterator<GLShape> iterator() {
		return objects.iterator();
	}

}
